package com.xuexuehai.carrecommand.service.impl;

import com.xuexuehai.carrecommand.config.RabbitMqConfig;
import com.xuexuehai.carrecommand.dao.IPrivsDao;
import com.xuexuehai.carrecommand.dao.IRpDao;
import com.xuexuehai.carrecommand.entity.Privs;
import com.xuexuehai.carrecommand.entity.Rp;
import com.xuexuehai.carrecommand.entity.User;
import com.xuexuehai.carrecommand.dao.IUserDao;
import com.xuexuehai.carrecommand.exception.enums.SysStatusEnums;
import com.xuexuehai.carrecommand.exception.user.AccountNotNullException;
import com.xuexuehai.carrecommand.exception.user.InvalidUserException;
import com.xuexuehai.carrecommand.exception.user.UsernameNotFoundException;
import com.xuexuehai.carrecommand.exception.user.UsernameUsedException;
import com.xuexuehai.carrecommand.service.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.naming.Name;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;


/**
 * @author xuexuehai
 * @mailBox xuehai.xue@QQ.com
 * @date 2021/8/18 11:25 下午
 */
@Service
@Transactional
@Slf4j
public class UserServiceImpl implements IUserService {
    @Autowired
    private IUserDao iUserDao;
    @Autowired
    private IRpDao iRpDao;
    @Autowired
    private IPrivsDao iPrivsDao;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private RabbitTemplate rabbitTemplate;


    @Override
    public List<Privs> login(String username, String password) {
        //登录验证
        if (username == null || "".equals(username.trim()) || password == null || "".equals(password.trim()))
            throw new AccountNotNullException(SysStatusEnums.ACCOUNT_NOT_NULL.getMsg());
//        List<User> users = iUserDao.findUserByUsername(username);
//        if(users == null || users.isEmpty())
//            throw new UsernameNotFoundException(SysStatusEnums.USERNAME_NOT_FOUND.getMsg());

        if(!isAvailableUsername(username)){
            throw new UsernameNotFoundException(SysStatusEnums.USERNAME_NOT_FOUND.getMsg());
        }

        User user = iUserDao.findUserByUsernameAndPassword(username, password);
        if (user == null)
            throw new InvalidUserException(SysStatusEnums.INVALID_USER.getMsg());

        //权限验证
        int roleId = user.getUserRole();
        //获取角色与权限的验证
        List<Rp> rps = iRpDao.findRpByRpRole(roleId);

        List<Privs> ps = new ArrayList<>();
        //遍历rps，获取角色对应的权限
        for (Rp rp : rps) {
            int privsId = rp.getRpPrivs();
            Privs privs = iPrivsDao.findById(privsId).get();
            ps.add(privs);
        }

        return ps;
    }

    @Override
    public boolean isAvailableUsername(String username) {
        if (redisTemplate.opsForList().size("usernames") == 0) {
            List<String> usernames = iUserDao.findAllUsername(username);
            for (int i = 0; i < usernames.size(); i++) {
                redisTemplate.opsForList().leftPush("usernames",usernames.get(i));
            }
        }

        List<Object> list = redisTemplate.opsForList().range("usernames",
                0,
                redisTemplate.opsForList().size("usernames"));

        return list.contains(username);
    }

    @Override
    public void registerUser(User user) {
        //在队列中验证用户名是否被使用
        if(user == null ||user.getUsername() == null){
            throw new AccountNotNullException(SysStatusEnums.ACCOUNT_NOT_NULL.getMsg());
        }
        if (redisTemplate.opsForList().size("usernames") != 0) {
            //如果缓存非空，获取缓存中的用户名
            List<Object> list = redisTemplate.opsForList().range("usernames",
                    0,
                    redisTemplate.opsForList().size("usernames"));

            //比较缓存中的用户名是否和当前申请注册的用户的用户名相同，相同抛出异常交由UserExceptionHandler处理
            for(Object obj : list){
                String currentUsername = (String) obj;
                if(currentUsername.equals(user.getUsername())){
                    throw new UsernameUsedException(SysStatusEnums.USERNAME_USED.getMsg());
                }
            }
        }

        //当用户名没有出现重复时，将当前需要注册的用户写入rabbit message queue中，交由mq的消费者存入数据库
        //rabbitTemplate.convertAndSend("direct_queue",user);
        rabbitTemplate.convertAndSend(RabbitMqConfig.TOPIC_EXCHANGE,"user.topic.register",user);
        log.debug("--->消息队列生产者将实例存储入mq, 当前时间: " + LocalDateTime.now());
    }

    @Override
    @RabbitListener(queues = {"queue_a","queue_b"})
    public void consumerRegisterUserMQ(User user) {
        //将当前队列中的用户插入数据库
        iUserDao.save(user);
        log.debug("--->消息队列消费mq中的user实例, 当前时间：" + LocalDateTime.now());

        /* todo:
        *  想法是是用邮件api，实现用户消费的异步存储
        *
        *  当前是流程是: 用户注册 之后 存入消息队列中, 由本方法实现队列中待消费用户的消费,消费完成之后直接使用 websocket
        *              从后端直接向前端推送消息进行消费
        *  优化思路以及流程:
        *   1. 当前方案中, 虽然有初步的异步构想, 但是并不是纯异步的方式实现告知用户的通知, 一般情况用户并不会在注册页面等待消息的传达
        *   2. 因此, 想要实现完全异步在后端服务中, 当用户信息通过 registry 存入用户注册的消息队列后
        *   使用 websocket 从后端进行消息推送, 告知用户当前的用户注册信息已经收到;
        *   当用户的信息从消息队列中被真正的消费之后, 再调用第三方邮件或者短信 API 实现用户的告知.
        *   3. 告知用户的邮件或者短信 API 解决方案:
        *       链接参考: https://www.kewail.com/doc/email/index.html
        */
    }
}
